package exec;

import java.util.HashMap;

import jmetest.renderer.ShadowTweaker;

import com.jme.app.SimplePassGame;
import com.jme.app.AbstractGame.ConfigShowMode;
import com.jme.bounding.BoundingBox;
import com.jme.bounding.BoundingSphere;
import com.jme.input.ChaseCamera;
import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.MouseInput;
import com.jme.input.action.InputActionEvent;
import com.jme.input.action.MouseInputAction;
import com.jme.input.thirdperson.ThirdPersonMouseLook;
import com.jme.light.DirectionalLight;
import com.jme.light.PointLight;
import com.jme.math.FastMath;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.pass.ShadowedRenderPass;
import com.jme.scene.Circle;
import com.jme.scene.Line;
import com.jme.scene.Node;
import com.jme.scene.VBOInfo;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Cylinder;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.LightState;
import com.jme.scene.state.MaterialState;
import com.jmex.bui.BTextField;
import com.jmex.bui.BWindow;
import com.jmex.bui.BuiSystem;
import com.jmex.bui.PolledRootNode;
import com.jmex.bui.layout.GroupLayout;
import com.jmex.model.collada.schema.ambientType;

public class SnookerTable2 extends SimplePassGame
{
	protected Node table;
	protected Node cueBall;
	protected float tableWidth;
	protected float tableLength;
	protected float baulkLineY;
	protected float dRadius;
	protected float blackDistance;
	protected float ballRadius;
    private static ShadowedRenderPass sPass = new ShadowedRenderPass();

	
	protected float pocketRadius = .4f;
	
	protected float cushionWidth = .5f;
	protected float railWidth = .3f;

	protected ColorRGBA tableGreen = new ColorRGBA(.01f, .5f, .01f, 1);
	protected ColorRGBA cushionGreen = new ColorRGBA(.01f, .5f, .01f, 1);
	protected ColorRGBA railBrown = new ColorRGBA(.25f, .25f, .01f, 1);
	protected ColorRGBA cueBrown = new ColorRGBA(.2f, .2f, .01f, 1);
	
	protected enum BALL_TYPE
	{
		CUE(new ColorRGBA(.9f, .9f, .9f, 1)),
		RED(new ColorRGBA(.5f, 0.1f, 0.1f, 1)),
		YELLOW(ColorRGBA.yellow),
		GREEN(ColorRGBA.green),
		BROWN(ColorRGBA.brown),
		BLUE(ColorRGBA.blue),
		PINK(ColorRGBA.pink),
		BLACK(new ColorRGBA(.1f, .1f, .1f, 1));
		
		private final ColorRGBA color;
		BALL_TYPE(ColorRGBA color)
		{
			this.color = color;
		}
		
		public ColorRGBA getColor()
		{
			return color;
		}
	}


    /**
     * Entry point
     * @param args
     */
    public static void main(String[] args) {
        SnookerTable2 app = new SnookerTable2();
        //new ShadowTweaker(sPass).setVisible(true);
        
		app.setConfigShowMode(ConfigShowMode.AlwaysShow, SnookerTable.class
				.getClassLoader().getResource("exec/snooker.png"));
        app.start();
    }
    
    SnookerTable2() {
        stencilBits = 4; // we need a minimum stencil buffer at least.
        
    }

    /**
     * builds the scene.
     * 
     * @see com.jme.app.BaseGame#initGame()
     */
    protected void simpleInitGame() {
		// Standard dimensions on a Snooker table
		tableWidth = 17.78f;
		tableLength = 35.69f;
		
		baulkLineY = 7.37f;
		dRadius = 2.92f;
		blackDistance = 3.24f;
		
		ballRadius = .525f / 2f;
		
		display.getRenderer().setBackgroundColor(ColorRGBA.gray);

		
		// Standard intiial ball positions
		Vector2f brownSpot = new Vector2f(tableWidth/2f, baulkLineY);
		Vector2f yellowSpot = new Vector2f(brownSpot.x + dRadius, brownSpot.y);
		Vector2f greenSpot = new Vector2f(brownSpot.x - dRadius, brownSpot.y);
		Vector2f blueSpot = new Vector2f(tableWidth/2f, tableLength/2f);
		Vector2f pinkSpot = new Vector2f(tableWidth/2f, tableLength*(3f/4f));
		Vector2f blackSpot = new Vector2f(tableWidth/2f, tableLength - blackDistance);
		
		Vector2f[] redSpots = new Vector2f[15];
		int i = 0;
		for (int y = 0; y < 5; y++)
		{
			for (int x = -y; x <= y; x += 2)
			{
				redSpots[i++] = new Vector2f(pinkSpot.x + x * ballRadius, pinkSpot.y + 2f * ballRadius + y * FastMath.sqrt(3) * ballRadius);
			}
		}
		
		
		// Arbritary cue ball position in D
		Vector2f cuePosition = new Vector2f(brownSpot.x + dRadius/2f, brownSpot.y - dRadius/2f);
		
		
		
		table = createTableNode(tableWidth, tableLength);
		
		addBall(yellowSpot, BALL_TYPE.YELLOW);
		addBall(greenSpot, BALL_TYPE.GREEN);
		addBall(brownSpot, BALL_TYPE.BROWN);
		addBall(blueSpot, BALL_TYPE.BLUE);
		addBall(pinkSpot, BALL_TYPE.PINK);
		addBall(blackSpot, BALL_TYPE.BLACK);
		
		for (Vector2f redSpot : redSpots)
		{
			addBall(redSpot, BALL_TYPE.RED);
		}
		
		cueBall = addBall(cuePosition , BALL_TYPE.CUE);
		
		
		
		input = createBallSetter(cueBall);
		// input=createTableChaser(tableNode);

		rootNode.attachChild(table); // Put it in the scene graph
		rootNode.attachChild(BuiSystem.getRootNode());
		rootNode.setRenderState(createLighting());

        sPass.add(rootNode);
        sPass.addOccluder(table);
        sPass.setRenderShadows(true);
        sPass.setLightingMethod(ShadowedRenderPass.LightingMethod.Additive);

		
		rootNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
        

        pManager.add(sPass);
    }
    
	protected void createGUI()
	{
		BuiSystem.init(new PolledRootNode(timer, input), "/rsrc/style.bss");

		BuiSystem.addWindow(createMainMenu());
	}

	protected BWindow createMainMenu()
	{
		BWindow mainMenu = new BWindow(BuiSystem.getStyle(), GroupLayout
				.makeVStretch());
		mainMenu.setSize(500, 100);

		// create a new BButton that displays "Back" and has an "actionMessage"
		// of "back"
		BTextField in = new BTextField();
		mainMenu.add(in);
		in.requestFocus();

		return mainMenu;
	}

	protected Node createTableNode(float tableWidth, float tableLength)
	{
		Node tableNode = new Node("Table node");
		// tableNode.setLocalTranslation(new Vector3f(0,0,0));
		Box b = new Box("Table", new Vector3f(0, -.05f, 0), tableWidth / 2f + cushionWidth, .05f,
				tableLength / 2f + cushionWidth);
		// Give the box a bounds object to allow it to be culled
		b.setModelBound(new BoundingSphere());
		// Calculate the best bounds for the object you gave it
		b.updateModelBound();

		/*
		 * TABLE CLOTH
		 */
		
		MaterialState tableMat = display.getRenderer().createMaterialState();
		tableMat.setAmbient(tableGreen.clone());
		tableMat.setDiffuse(tableGreen.clone());
		tableMat.setSpecular(ColorRGBA.black.clone());
		tableMat.setShininess(.5f);
		tableMat.setEmissive(ColorRGBA.black.clone());
		tableMat.setEnabled(true);
		

		b.setRenderState(tableMat);
		tableNode.attachChild(b);
		

		float minX = -tableWidth / 2f;
		float maxX = tableWidth / 2f;
		float minY = -tableLength / 2f;
		float maxY = tableLength / 2f;
		
		float minZ = -.5f;
		float maxZ = .5f;
		
		/*
		 * CUSHIONS
		 */
		Box bottomCushion = new Box("Bottom cushion", new Vector3f(minX, minZ,
				minY - cushionWidth), new Vector3f(maxX, maxZ, minY));
		Box topCushion = new Box("Top cushion", new Vector3f(minX, minZ, maxY),
				new Vector3f(maxX, maxZ, maxY + cushionWidth));
		Box bottomLeftCushion = new Box("Bottom left cushion", new Vector3f(
				minX - cushionWidth, minZ, minY), new Vector3f(minX, maxZ,
				-pocketRadius));
		Box topLeftCushion = new Box("Top left cushion", new Vector3f(minX
				- cushionWidth, minZ, pocketRadius), new Vector3f(minX, maxZ,
				maxY));
		Box bottomRightCushion = new Box("Bottom right cushion", new Vector3f(
				maxX, minZ, minY), new Vector3f(maxX + cushionWidth, maxZ,
				-pocketRadius));
		Box topRightCushion = new Box("Top right cushion", new Vector3f(maxX,
				minZ, pocketRadius), new Vector3f(maxX + cushionWidth, maxZ,
				maxY));
		
		Box[] cushions = new Box[] { bottomCushion, topCushion, bottomLeftCushion,
				topLeftCushion, bottomRightCushion, topRightCushion };

		MaterialState cushionMat = display.getRenderer().createMaterialState();
		cushionMat.setAmbient(cushionGreen.clone());
		cushionMat.setDiffuse(cushionGreen.clone());
		cushionMat.setSpecular(cushionGreen.clone());
		cushionMat.setShininess(70f);
		cushionMat.setEmissive(ColorRGBA.black.clone());
		cushionMat.setEnabled(true);
		
		for (Box cushion : cushions)
		{
			cushion.setRenderState(cushionMat);
			tableNode.attachChild(cushion);
		}
		
		/*
		 * RAILS
		 */
		
		// names are wrong because x dimension is mirrored?
		// six separate rails
		Box bottomRail = new Box("Bottom rail", new Vector3f(minX, minZ, minY
				- cushionWidth - railWidth), new Vector3f(maxX, maxZ, minY
				- cushionWidth));
		Box topRail = new Box("Top rail", new Vector3f(minX, minZ, maxY
				+ cushionWidth), new Vector3f(maxX, maxZ, maxY + cushionWidth
				+ railWidth));
		Box bottomLeftRail = new Box("Bottom left rail", new Vector3f(minX
				- cushionWidth - railWidth, minZ, minY), new Vector3f(minX
				- cushionWidth, maxZ, -pocketRadius));
		Box topLeftRail = new Box("Top left rail", new Vector3f(minX
				- cushionWidth - railWidth, minZ, pocketRadius), new Vector3f(minX
				- cushionWidth, maxZ, maxY));
		Box bottomRightRail = new Box("Bottom right rail", new Vector3f(maxX
				+ cushionWidth, minZ, minY), new Vector3f(maxX
				+ cushionWidth + railWidth, maxZ, -pocketRadius));
		Box topRightRail = new Box("Top right rail", new Vector3f(maxX
				+ cushionWidth, minZ, pocketRadius), new Vector3f(maxX
				+ cushionWidth + railWidth, maxZ, maxY));
		
		// one connected rail
//		Box bottomRail = new Box("Bottom rail", new Vector3f(minX
//				- cushionWidth - railWidth, minZ, minY - cushionWidth
//				- railWidth), new Vector3f(maxX + cushionWidth + railWidth,
//				maxZ, minY - cushionWidth));
//		Box topRail = new Box("Top rail", new Vector3f(minX - cushionWidth
//				- railWidth, minZ, maxY + cushionWidth), new Vector3f(maxX
//				+ cushionWidth + railWidth, maxZ, maxY + cushionWidth
//				+ railWidth));
//		Box bottomLeftRail = new Box("Bottom left rail", new Vector3f(minX
//				- cushionWidth - railWidth, minZ, minY - cushionWidth
//				- railWidth), new Vector3f(minX - cushionWidth, maxZ, 0));
//		Box topLeftRail = new Box("Top left rail", new Vector3f(minX
//				- cushionWidth - railWidth, minZ, 0), new Vector3f(minX
//				- cushionWidth, maxZ, maxY + cushionWidth + railWidth));
//		Box bottomRightRail = new Box("Bottom right rail", new Vector3f(maxX
//				+ cushionWidth, minZ, minY - cushionWidth - railWidth),
//				new Vector3f(maxX + cushionWidth + railWidth, maxZ, 0));
//		Box topRightRail = new Box("Top right rail", new Vector3f(maxX
//				+ cushionWidth, minZ, 0), new Vector3f(maxX + cushionWidth
//				+ railWidth, maxZ, maxY + cushionWidth + railWidth));

		MaterialState railMat = display.getRenderer().createMaterialState();
		railMat.setAmbient(railBrown.clone());
		railMat.setDiffuse(railBrown.clone());
		railMat.setSpecular(railBrown.clone());
		railMat.setShininess(70f);
		railMat.setEmissive(ColorRGBA.black.clone());
		railMat.setEnabled(true);

		Box[] rails = new Box[] { bottomRail, topRail, bottomLeftRail,
				topLeftRail, bottomRightRail, topRightRail };
		
		for (Box rail : rails)
		{
			rail.setRenderState(railMat);
			tableNode.attachChild(rail);
		}
		
		/*
		 * MARKINGS
		 */
		
		Line baulkLine = new Line("baulkLine", new Vector3f[]{new Vector3f(-tableWidth/2f, .001f, -tableLength/2f + baulkLineY), new Vector3f(tableWidth/2f, .001f, -tableLength/2f + baulkLineY)}, null, null, null);
		baulkLine.setLineWidth(3f);
		baulkLine.setAntialiased(true);
		MaterialState baulkLineMat = display.getRenderer().createMaterialState();
		ColorRGBA baulkLineColor = new ColorRGBA(.5f, .9f, .5f, .5f);
		baulkLineMat.setAmbient(baulkLineColor.clone());
		baulkLineMat.setDiffuse(baulkLineColor.clone());
		baulkLineMat.setSpecular(baulkLineColor.clone());
		baulkLineMat.setShininess(0);
		baulkLineMat.setEmissive(baulkLineColor.clone());
		baulkLineMat.setEnabled(true);
		baulkLine.setRenderState(baulkLineMat);
		
		tableNode.attachChild(baulkLine);
		
		Circle d = new Circle("D", 50, dRadius);
		d.getLocalRotation().fromAngles(FastMath.PI/2f, 0, 0);
		d.setLocalTranslation(0, 0, -tableLength/2f + baulkLineY);
		
		// hack makes half of the circle kind of disappear
//		d.getLocalRotation().fromAngles(FastMath.PI/2f + .03f, 0, 0);
//		d.setLocalTranslation(0, -.02f, -tableHeight/2f + baulkLineY);
		
		d.setLineWidth(3f);
		d.setAntialiased(true);
		MaterialState dMat = display.getRenderer().createMaterialState();
		ColorRGBA dColor = baulkLineColor;
		dMat.setAmbient(dColor.clone());
		dMat.setDiffuse(dColor.clone());
		dMat.setSpecular(dColor.clone());
		dMat.setShininess(0);
		dMat.setEmissive(dColor.clone());
		dMat.setEnabled(true);
		d.setRenderState(dMat);
		
		tableNode.attachChild(d);

		return tableNode;
	}
	
	// x, y starting from bottom left corner instead of table center
	protected Node addBall(float x, float y, BALL_TYPE type)
	{
		Node ballNode = createBallNode((-tableWidth/2f) + x, (-tableLength/2f) + y, type);
		table.attachChild(ballNode);
		return ballNode;
	}

	protected Node addBall(Vector2f p, BALL_TYPE type)
	{
		return addBall(p.x, p.y, type);
	}

	protected Node createBallNode(float x, float y, BALL_TYPE type)
	{
		Node ballNode = new Node("Ball");
		Sphere s = new Sphere("Ball Sphere", 15, 15, ballRadius);
		s.setModelBound(new BoundingBox());
	    s.updateModelBound();
	 
	    //s.setVBOInfo(new VBOInfo(true));
	        
		MaterialState ballMat = display.getRenderer().createMaterialState();

		ColorRGBA typeColor = type.getColor();

		ballMat.setAmbient(typeColor.clone());
		ballMat.setDiffuse(typeColor.clone());
		ballMat.setSpecular(typeColor.clone());
		ballMat.setShininess(70f);
		ballMat.setEmissive(ColorRGBA.black.clone());
		ballMat.setEnabled(true);

		s.setRenderState(ballMat);
        //sPass.addOccluder(s);

		ballNode.setLocalTranslation(new Vector3f(x, ballRadius, y));
		ballNode.attachChild(s);
		return ballNode;
	}

	protected InputHandler createTableChaser(Node table)
	{
		class Handler extends MouseInputAction
		{
			private boolean goNext = false;

			@Override
			public void performAction(InputActionEvent evt)
			{
				if (MouseInput.get().isButtonDown(0))
				{
					goNext = true;
				}
				else if (goNext)
				{
					input = createBallSetter(cueBall);
				}
			}

		}
		HashMap<String, Object> props = new HashMap<String, Object>();

		props.put(ThirdPersonMouseLook.PROP_MAXROLLOUT, "30");
		props.put(ThirdPersonMouseLook.PROP_MINROLLOUT, "15");
		props.put(ThirdPersonMouseLook.PROP_MINASCENT, "" + 30
				* FastMath.DEG_TO_RAD);
		props.put(ThirdPersonMouseLook.PROP_MAXASCENT, "" + 30
				* FastMath.DEG_TO_RAD);
		props.put(ChaseCamera.PROP_INITIALSPHERECOORDS, new Vector3f(30, 0,
				30 * FastMath.DEG_TO_RAD));
		props.put(ChaseCamera.PROP_TARGETOFFSET, new Vector3f(0, -3, 0));

		ChaseCamera chaser = new ChaseCamera(cam, table, props);
		chaser.addAction(new Handler());
		chaser.setMaxDistance(30);
		chaser.setMinDistance(15);

		return chaser;
	}

	protected InputHandler createBallSetter(final Node ball)
	{

		class Mover extends MouseInputAction
		{
			private boolean goNext = false;

			@Override
			public void performAction(InputActionEvent evt)
			{
				if (MouseInput.get().isButtonDown(0))
				{
					goNext = true;
				}
				else if (goNext)
				{
					input = createCueSetter(ball);
				}
				
				float transX = -MouseInput.get().getXDelta() * 0.01f;
				float transY = MouseInput.get().getYDelta() * 0.01f;
				
				// This adjusts transX and transY so that the ball will still be
				// in the table. This is smoother than only checking the new
				// coordinates and moving the ball if they are valid.
				
				float newX = ball.getLocalTranslation().x + transX;
				float newY = ball.getLocalTranslation().z + transY;
				
				float minX = -tableWidth/2f + ballRadius;
				float maxX = tableWidth/2f - ballRadius;
				float minY = -tableLength/2f + ballRadius;
				float maxY = tableLength/2f - ballRadius;
				
				newX = Math.max(newX, minX);
				newX = Math.min(newX, maxX);
				newY = Math.max(newY, minY);
				newY = Math.min(newY, maxY);

				ball.getLocalTranslation().setX(newX);
				ball.getLocalTranslation().setZ(newY);
				
				ball.updateGeometricState(evt.getTime(), true);
			}

		}
		HashMap<String, Object> props = new HashMap<String, Object>();

		props.put(ThirdPersonMouseLook.PROP_MAXROLLOUT, "30");
		props.put(ThirdPersonMouseLook.PROP_MINROLLOUT, "15");
		props.put(ThirdPersonMouseLook.PROP_MINASCENT, "" + 60
				* FastMath.DEG_TO_RAD);
		props.put(ThirdPersonMouseLook.PROP_MAXASCENT, "" + 60
				* FastMath.DEG_TO_RAD);
		props.put(ChaseCamera.PROP_STAYBEHINDTARGET, true);

		props.put(ChaseCamera.PROP_INITIALSPHERECOORDS, new Vector3f(30, 0,
				60 * FastMath.DEG_TO_RAD));
		props.put(ChaseCamera.PROP_TARGETOFFSET, new Vector3f(0, -3, 0));

		ChaseCamera chaser = new ChaseCamera(cam, ball, props)
		{
			@Override
			protected void setupMouse()
			{
			}
		};
		chaser.addAction(new Mover());
		chaser.setMaxDistance(30);
		chaser.setMinDistance(15);

		return chaser;
	}

	protected InputHandler createCueSetter(final Node ball)
	{
		final Cylinder cue = new Cylinder("Cue", 30, 30, .06f, 6f, true);
		MaterialState cueMat = display.getRenderer().createMaterialState();
		cueMat.setAmbient(cueBrown.clone());
		cueMat.setDiffuse(cueBrown.clone());
		cueMat.setSpecular(cueBrown.clone());
		cueMat.setShininess(0);
		cueMat.setEmissive(cueBrown.clone());
		cueMat.setEnabled(true);
		cue.setRenderState(cueMat);
		
		Sphere aimingPoint = new Sphere("Aiming point",5,5,.025f);
		
		MaterialState pointMat = display.getRenderer().createMaterialState();
		pointMat.setAmbient(ColorRGBA.red.clone());
		pointMat.setDiffuse(ColorRGBA.red.clone());
		pointMat.setSpecular(ColorRGBA.red.clone());
		pointMat.setShininess(.5f);
		pointMat.setEmissive(ColorRGBA.red.clone());
		pointMat.setEnabled(true);
		
		aimingPoint.setRenderState(pointMat);

		
		final Node cueNode = new Node();
		final Node aimingPointNode = new Node();
		cueNode.setLocalTranslation(ball.getLocalTranslation());
		cueNode.attachChild(cue);
		cueNode.attachChild(aimingPointNode);
		table.attachChild(cueNode);
		aimingPointNode.attachChild(aimingPoint);
		aimingPoint.setLocalTranslation(0,0,-ballRadius);
		cue.setLocalTranslation(0, 0, -3.5f);
		
		cueNode.updateRenderState();
		
		HashMap<String, Object> props = new HashMap<String, Object>();

		props.put(ThirdPersonMouseLook.PROP_MAXROLLOUT, "20");
		props.put(ThirdPersonMouseLook.PROP_MINROLLOUT, "10");
		props.put(ThirdPersonMouseLook.PROP_MINASCENT, "" + 30
				* FastMath.DEG_TO_RAD);
		props.put(ThirdPersonMouseLook.PROP_MAXASCENT, "" + 30
				* FastMath.DEG_TO_RAD);
		// props.put(ThirdPersonMouseLook.PROP_INVERTROTATE, true);
		props.put(ChaseCamera.PROP_STAYBEHINDTARGET, true);

		props.put(ChaseCamera.PROP_INITIALSPHERECOORDS, new Vector3f(15, 0,
				30 * FastMath.DEG_TO_RAD));
		props.put(ChaseCamera.PROP_TARGETOFFSET, new Vector3f(0, -1, 0));

		final ChaseCamera chaser = new ChaseCamera(cam, cueNode, props)
		{
			@Override
			protected void setupMouse()
			{
			}
		};
		
		class Mover extends MouseInputAction
		{
			private float angX = 0;
			private float angY = 0;

			private boolean goNext = false;

			@Override
			public void performAction(InputActionEvent evt)
			{
				if (MouseInput.get().isButtonDown(0))
				{
					goNext = true;
					float transY = MouseInput.get().getYDelta() * 0.005f;
					cue.getLocalTranslation().addLocal(0, 0, transY);
				}
				else if (goNext)
				{
					table.detachChild(cueNode);
					input = createTableChaser(table);
				}
				else
				{
					float transX = -MouseInput.get().getXDelta() * 0.005f;
					float transY = MouseInput.get().getYDelta() * 0.005f;
	
					if (MouseInput.get().isButtonDown(1))
					{
						chaser.setMaxDistance(5);
						chaser.setMinDistance(4);
						
						float newX = cue.getLocalTranslation().x + transX;
						float newY = cue.getLocalTranslation().y + transY;
						
						if(FastMath.sqr(newX)+FastMath.sqr(newY) > FastMath.sqr(ballRadius))
						{
							float angle = FastMath.atan2(newY, newX);
							transX = ballRadius * FastMath.cos(angle) - cue.getLocalTranslation().x;
							transY = ballRadius * FastMath.sin(angle) - cue.getLocalTranslation().y;
						}
						cue.getLocalTranslation().addLocal(transX,transY,0);
						aimingPointNode.getLocalRotation().fromAngles(FastMath.asin(cue.getLocalTranslation().y/ballRadius), -FastMath.asin(cue.getLocalTranslation().x/ballRadius), 0);
					}
					else
					{
						chaser.setMaxDistance(20);
						chaser.setMinDistance(10);
						angX -= transX;
						if (transY + angY < FastMath.PI / 2 && transY + angY > 0)
						{
							angY += transY;
						}
						cueNode.getLocalRotation().fromAngles(angY, angX, 0);
					}
				}
				cueNode.updateGeometricState(evt.getTime(), true);
			}

		}

		chaser.addAction(new Mover());
		chaser.setMaxDistance(20);
		chaser.setMinDistance(10);

		return chaser;
	}

	protected LightState createLighting()
	{
		/** Set up a basic, default light. */
		DirectionalLight light = new DirectionalLight();
		light.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
		light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
		light.setDirection(new Vector3f(.5f, -1f, 1f));
		light.setEnabled(true);
		light.setShadowCaster(true);
		

		/** Attach the light to a lightState and the lightState to rootNode. */
		LightState lightState = display.getRenderer().createLightState();
		lightState.setEnabled(true);
		lightState.attach(light);

		return lightState;
	}

    protected void simpleUpdate() {

		if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit"))
		{
			finished = true;
		}
		pManager.updatePasses(tpf);
        input.update(tpf);

    }

}
